package com.fxf.srb.core.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fxf.common.exception.BusinessException;
import com.fxf.srb.core.enums.BorrowInfoStatusEnum;
import com.fxf.srb.core.enums.LendStatusEnum;
import com.fxf.srb.core.enums.ReturnMethodEnum;
import com.fxf.srb.core.enums.TransTypeEnum;
import com.fxf.srb.core.hfb.FormHelper;
import com.fxf.srb.core.hfb.HfbConst;
import com.fxf.srb.core.hfb.RequestHelper;
import com.fxf.srb.core.mapper.UserAccountMapper;
import com.fxf.srb.core.mapper.UserInfoMapper;
import com.fxf.srb.core.pojo.bo.TransFlowBO;
import com.fxf.srb.core.pojo.entity.*;
import com.fxf.srb.core.mapper.LendMapper;
import com.fxf.srb.core.pojo.vo.BorrowInfoApprovalVO;
import com.fxf.srb.core.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fxf.srb.core.util.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 标的准备表 服务实现类
 * </p>
 *
 * @author fxf
 * @since 2021-05-26
 */
@Service
@Slf4j
public class LendServiceImpl extends ServiceImpl<LendMapper, Lend> implements ILendService {

    @Resource
    private IDictService iDictService;

    @Resource
    private IBorrowerService iBorrowerService;

    @Resource
    private UserAccountMapper userAccountMapper;

    @Resource
    private UserInfoMapper userInfoMapper;

    @Resource
    private ITransFlowService iTransFlowService;

    @Resource
    private ILendItemService iLendItemService;

    @Resource
    private ILendReturnService lendReturnService;

    @Resource
    private ILendItemReturnService lendItemReturnService;

    /**
     * 生成新的标的
     * @param borrowInfoApprovalVO
     * @param borrowInfo
     */
    @Override
    public void createLend(BorrowInfoApprovalVO borrowInfoApprovalVO, BorrowInfo borrowInfo) {
        Lend lend = new Lend();
        lend.setUserId(borrowInfo.getUserId());
        lend.setBorrowInfoId(borrowInfo.getId());
        lend.setLendNo(LendNoUtils.getLendNo());
        lend.setTitle(borrowInfoApprovalVO.getTitle());
        lend.setAmount(borrowInfo.getAmount());
        lend.setPeriod(borrowInfo.getPeriod());
        lend.setLendYearRate(borrowInfo.getBorrowYearRate().divide(new BigDecimal(100)));
        lend.setServiceRate(borrowInfoApprovalVO.getServiceRate().divide(new BigDecimal(100)));
        lend.setReturnMethod(borrowInfo.getReturnMethod());
        lend.setLowestAmount(new BigDecimal(100));
        lend.setInvestAmount(new BigDecimal(0));
        lend.setInvestNum(0);
        lend.setPublishDate(LocalDateTime.now());

        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDate lendStartDate = LocalDate.parse(borrowInfoApprovalVO.getLendStartDate());
        lend.setLendStartDate(lendStartDate);

        //结束日期
        LocalDate lendEndDate = lendStartDate.plusMonths(borrowInfo.getPeriod());
        lend.setLendEndDate(lendEndDate);

        lend.setLendInfo(borrowInfoApprovalVO.getLendInfo());//描述

        //平台预期收益
        //        月年化 = 年化 / 12
        BigDecimal monthRate = lend.getServiceRate().divide(new BigDecimal(12), 8, BigDecimal.ROUND_DOWN);
        //        平台收益 = 标的金额 * 月年化 * 期数
        BigDecimal expectAmount = lend.getAmount().multiply(monthRate).multiply(new BigDecimal(lend.getPeriod()));

        //实际收益
        lend.setRealAmount(new BigDecimal(0));
        //状态
        lend.setStatus(LendStatusEnum.INVEST_RUN.getStatus());
        //审核时间
        lend.setCheckTime(LocalDateTime.now());

        //审核人
        lend.setCheckAdminId(1L);

        baseMapper.insert(lend);

    }

    /**
     * 查询标的列表
     * @return
     */
    @Override
    public List<Lend> selectList() {

        List<Lend> lends = baseMapper.selectList(null);
        lends.forEach(lend -> {
            String msgByStatus = LendStatusEnum.getMsgByStatus(lend.getStatus());
            String returnMethod = iDictService.getNameByParentDictCodeAndValue("returnMethod", lend.getReturnMethod());
            lend.getParam().put("status",msgByStatus);


        });
        return null;
    }

    @Override
    public Map<String, Object> getLendDetail(Long id) {
        //查询标的信息
        Lend lend = baseMapper.selectById(id);

        String returnMethod = iDictService.getNameByParentDictCodeAndValue("returnMethod",lend.getReturnMethod());
        String msgByStatus = LendStatusEnum.getMsgByStatus(lend.getStatus());
        lend.getParam().put("status",msgByStatus);
        lend.getParam().put("returnMethod",returnMethod);

        //根据还款人id获取还款人信息
        QueryWrapper<Borrower> wrapper = new QueryWrapper<>();
        wrapper.eq("user_id",lend.getUserId());
        Borrower borrower = iBorrowerService.getOne(wrapper);
        iBorrowerService.getBorrowerDetailsVoById(borrower.getId());

        return null;
    }

    /**
     * 计算投资人收益
     * @param invest
     * @param yearRate
     * @param totalmonth
     * @param returnMethod
     * @return
     */
    @Override
    public BigDecimal getInterestCount(BigDecimal invest, BigDecimal yearRate, Integer totalmonth, Integer returnMethod) {
        BigDecimal interestCount;

        if (returnMethod.intValue() == ReturnMethodEnum.ONE.getMethod()){
           interestCount = Amount1Helper.getInterestCount(invest, yearRate, totalmonth);
        }else if (returnMethod.intValue() == ReturnMethodEnum.TWO.getMethod()){
            interestCount = Amount2Helper.getInterestCount(invest, yearRate, totalmonth);
        }else if (returnMethod.intValue() == ReturnMethodEnum.THREE.getMethod()){
            interestCount = Amount3Helper.getInterestCount(invest, yearRate, totalmonth);
        }else {
            interestCount = Amount4Helper.getInterestCount(invest, yearRate, totalmonth);
        }
        return interestCount;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void makeLoan(Long id) {
        Lend lend = baseMapper.selectById(id);
        Map<String, Object> paramMaps = new HashMap<>();
        paramMaps.put("agent_id", HfbConst.AGENT_ID);
        paramMaps.put("agent_project_code",lend.getLendNo());
        paramMaps.put("agent_bill_no",LendNoUtils.getLoanNo());
//        paramMaps.put("mch_fee",)
        //平台收益，放款扣除，借款人借款实际金额=借款金额-平台收益
        //月年化
        BigDecimal monthRate = lend.getServiceRate().divide(new BigDecimal(12), 8, BigDecimal.ROUND_DOWN);
        //平台实际收益 = 已投金额 * 月年化 * 标的期数
        BigDecimal mchFee = lend.getInvestAmount().multiply(monthRate).multiply(new BigDecimal(lend.getPeriod()));
        //商户手续费(平台实际收益)
        paramMaps.put("mch_fee",mchFee);
        paramMaps.put("note","");
        paramMaps.put("timestamp", RequestHelper.getTimestamp());
        String sign = RequestHelper.getSign(paramMaps);
        log.info("放款参数：" + JSONObject.toJSONString(paramMaps));
        paramMaps.put("sign",sign);

        //远程调用汇付宝接口
        JSONObject result = RequestHelper.sendRequest(paramMaps,HfbConst.MAKE_LOAD_URL);
        log.info("放款结果：" + result.toJSONString());
        if (!"0000".equals(result.getString("result_code"))){
            //放款失败
            throw new BusinessException(result.getString("resultMsg"));
        }

        //放款成功进行以下操作

        /**
         * （1）标的状态和标的平台收益
         * （2）给借款账号转入金额
         * （3）增加借款交易流水
         * （4）解冻并扣除投资人资金
         * （5）增加投资人交易流水
         * （6）生成借款人还款计划和出借人回款计划
         */
        lend.setStatus(LendStatusEnum.PAY_RUN.getStatus());
        lend.setRealAmount(mchFee);

        baseMapper.updateById(lend);


        Long userId = lend.getUserId();
        UserInfo userInfo = userInfoMapper.selectById(userId);
        String bindCode = userInfo.getBindCode();

        BigDecimal voteAmt = new BigDecimal(result.getString("vote_amt"));
        //给借款账号转入金额
        userAccountMapper.updateAccount(bindCode, voteAmt, new BigDecimal(0));
        //增加借款交易流水
        TransFlowBO transFlowBO = new TransFlowBO(
                result.getString("agent_bill_no"),
                bindCode,voteAmt,
                TransTypeEnum.BORROW_BACK,
                "借款放款到账，编号：" + lend.getLendNo());
        iTransFlowService.saveTransFlow(transFlowBO);

        //解冻并扣除投资人资金

        //获取投资人列表及投资信息
        List<LendItem> lendItemList = iLendItemService.selectByLendId(lend.getId(), 1);
        lendItemList.forEach(lendItem -> {
            //获取投资人信息
            Long investUserId = lendItem.getInvestUserId();
            UserInfo investUserInfo = userInfoMapper.selectById(investUserId);
            String investBindCode = investUserInfo.getBindCode();

            //解冻并扣除投资人金额
            BigDecimal investAmount = lendItem.getInvestAmount();
            userAccountMapper.updateAccount(investBindCode,new BigDecimal(0),investAmount.negate());

            //添加投资人流水
            TransFlowBO investTransFlowBO = new TransFlowBO(
                    LendNoUtils.getTransNo(),
                    investBindCode,investAmount,
                    TransTypeEnum.INVEST_UNLOCK,
                    "冻结资金转出，出借放款，编号：" + lend.getLendNo());//项目编号
            iTransFlowService.saveTransFlow(investTransFlowBO);
        });
        //放款成功生成借款人还款计划和投资人回款计划
        // TODO
        //放款成功生成借款人还款计划和投资人回款计划
        this.repaymentPlan(lend);

    }

    /**
     * 还款计划
     *
     * @param lend
     */
    private void repaymentPlan(Lend lend) {
        //还款计划列表
        ArrayList<LendReturn> lendReturnList = new ArrayList<>();

        //按还款时间生成还款计划
        int len = lend.getPeriod().intValue();
        for (int i = 0; i < len; i++) {
            LendReturn lendReturn = new LendReturn();

            lendReturn.setReturnNo(LendNoUtils.getReturnNo());
            lendReturn.setLendId(lend.getId());
            lendReturn.setBorrowInfoId(lend.getBorrowInfoId());
            lendReturn.setUserId(lend.getUserId());
            lendReturn.setAmount(lend.getAmount());
            lendReturn.setBaseAmount(lend.getInvestAmount());
            lendReturn.setLendYearRate(lend.getLendYearRate());
            lendReturn.setCurrentPeriod(i);//当前期数
            lendReturn.setReturnMethod(lend.getReturnMethod());

            //说明：还款计划中的这三项 = 回款计划中对应的这三项和：因此需要先生成对应的回款计划，然后进行更新
            //			lendReturn.setPrincipal();
            //			lendReturn.setInterest();
            //			lendReturn.setTotal();


            lendReturn.setFee(new BigDecimal(0));
            lendReturn.setReturnDate(lend.getLendStartDate().plusMonths(i)); //第二个月开始还款
            lendReturn.setOverdue(false);
            //判断还款计划是否为最后一期
            if (i == len){
                //标识为最后一次还款
                lendReturn.setLast(true);
            }else {
                lendReturn.setLast(false);
            }
            lendReturn.setStatus(0);
            lendReturnList.add(lendReturn);
        }
        //批量保存
        lendReturnService.saveBatch(lendReturnList);

        //生成期数和还款记录id对应的键值对集合
        Map<Integer, Long> lendReturnMap = lendReturnList.stream().collect(
                //取list集合中的每条还款记录中的还款期数为map的key和对应的还款记录id为value生成一个map集合
                Collectors.toMap(LendReturn::getCurrentPeriod, LendReturn::getId)
        );
        //创建所有投资的所有回款记录列表
        List<LendItemReturn> lendItemReturnAllList = new ArrayList<>();
        //获取当前标的下的所有已支付的投资
        List<LendItem> lendItemList = iLendItemService.selectByLendId(lend.getId(), 1);
        lendItemList.forEach(lendItem -> {
            //创建回款计划列表
            List<LendItemReturn> lendItemReturnList = this.returnInvest(lendItem.getId(), lendReturnMap, lend);
            lendItemReturnAllList.addAll(lendItemReturnList);
        });

        for (LendReturn lendReturn : lendReturnList) {
            //遍历所有回款计划，从每期回款计划中过滤出每期回款计划的本金，并从0加起来得到每期回款计划的总本金
            BigDecimal sumPrincipal = lendItemReturnAllList.stream()
                    .filter(item -> item.getLendReturnId().longValue() == lendReturn.getId().longValue())
                    .map(LendItemReturn::getPrincipal)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            //遍历所有回款计划，从每期回款计划中过滤出每期回款计划的利息，并从0加起来得到每期回款计划的总利息
            BigDecimal sumInterest = lendItemReturnAllList.stream()
                    .filter(item -> item.getLendReturnId().longValue() == lendReturn.getId().longValue())
                    .map(LendItemReturn::getInterest)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);

            BigDecimal sumTotal = lendItemReturnAllList.stream()
                    .filter(item -> item.getLendReturnId().longValue() == lendReturn.getId().longValue())
                    .map(LendItemReturn::getTotal)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);

            lendReturn.setPrincipal(sumPrincipal);
            lendReturn.setInterest(sumInterest);
            lendReturn.setTotal(sumTotal);
        }

    }

    /**
     * 回款计划
     *
     * @param lendItemId
     * @param lendReturnMap 还款期数与还款计划id对应map
     * @param lend
     * @return
     */
    public List<LendItemReturn> returnInvest(Long lendItemId, Map<Integer, Long> lendReturnMap, Lend lend) {

        //获取当前投资记录信息
        LendItem lendItem = iLendItemService.getById(lendItemId);

        //调用工具类计算还款本金和利息，存储为集合
        BigDecimal amount = lendItem.getInvestAmount();//投资金额
        BigDecimal yearRate = lendItem.getLendYearRate();//年化利率
        int totalMonth = lend.getPeriod();//期数

        Map<Integer, BigDecimal> mapInterest = null;  //还款期数 -> 利息
        Map<Integer, BigDecimal> mapPrincipal = null; //还款期数 -> 本金

        //根据还款方式计算本金和利息
        if (lend.getReturnMethod().intValue() == ReturnMethodEnum.ONE.getMethod()) {
            //利息
            mapInterest = Amount1Helper.getPerMonthInterest(amount, yearRate, totalMonth);
            //本金
            mapPrincipal = Amount1Helper.getPerMonthPrincipal(amount, yearRate, totalMonth);
        } else if (lend.getReturnMethod().intValue() == ReturnMethodEnum.TWO.getMethod()) {
            mapInterest = Amount2Helper.getPerMonthInterest(amount, yearRate, totalMonth);
            mapPrincipal = Amount2Helper.getPerMonthPrincipal(amount, yearRate, totalMonth);
        } else if (lend.getReturnMethod().intValue() == ReturnMethodEnum.THREE.getMethod()) {
            mapInterest = Amount3Helper.getPerMonthInterest(amount, yearRate, totalMonth);
            mapPrincipal = Amount3Helper.getPerMonthPrincipal(amount, yearRate, totalMonth);
        } else {
            mapInterest = Amount4Helper.getPerMonthInterest(amount, yearRate, totalMonth);
            mapPrincipal = Amount4Helper.getPerMonthPrincipal(amount, yearRate, totalMonth);
        }

        //创建汇款计划列表
        List<LendItemReturn> lendItemReturnList = new ArrayList<>();

        for (Map.Entry<Integer, BigDecimal> entry : mapInterest.entrySet()) {
            Integer currentPeriod = entry.getKey();
            //根据当前期数，获取还款计划id
            Long lendReturnId = lendReturnMap.get(currentPeriod);//还款计划id

            //创建回款计划记录
            LendItemReturn lendItemReturn = new LendItemReturn();
            //将还款计划关联到回款计划
            lendItemReturn.setLendReturnId(lendReturnId);//关联还款计划id
            //设置回款计划的基本属性
            lendItemReturn.setLendItemId(lendItemId);//标的项id
            lendItemReturn.setInvestUserId(lend.getUserId());//出借用户id---借款人id
            lendItemReturn.setLendId(lendItem.getLendId());//标的id
            lendItemReturn.setInvestAmount(lendItem.getInvestAmount());//出借金额--回款金额
            lendItemReturn.setLendYearRate(lend.getLendYearRate());//年化利率
            lendItemReturn.setCurrentPeriod(currentPeriod);//当前回款的期数
            lendItemReturn.setReturnMethod(lend.getReturnMethod());//回款方式
            //计算回款本金、利息和总额（注意最后一个月的计算）  判断当前期数和总期数是否相等，如果相等就是最后一期
            if (mapInterest.size() > 0 && currentPeriod.intValue() == lend.getPeriod().intValue()){

                BigDecimal investAmount1 = lendItem.getInvestAmount();//本金--投资金额

//                BigDecimal principalLast;//最后一期本金
//                for (int i = 0; i < currentPeriod.intValue(); i++) {
//                    BigDecimal investAmount = lendItemReturnList.get(i).getInvestAmount();
//                    principalLast = investAmount1.subtract(investAmount);
//                }

                //最后一期本金 = 本金 - 前几次之和
                //遍历回款计划列表，将每条回款计划记录的本金加起来
                BigDecimal sumPrincipal = lendItemReturnList.stream()
                        .map(LendItemReturn::getPrincipal)
                        //从0开始，将lendItemReturn对象中的本金加起来
                        //就是从0开始，将每条还款记录的本金加起来计算出前几次之和
                        .reduce(BigDecimal.ZERO,BigDecimal::add);
                BigDecimal lastPrincipal = investAmount1.subtract(sumPrincipal);
                lendItemReturn.setPrincipal(lastPrincipal);

                //利息
                BigDecimal sumInterest = lendItemReturnList.stream()
                        .map(LendItemReturn::getInterest)
                        //从0开始，将lendItemReturn对象中的本金加起来
                        //就是从0开始，将每条还款记录的本金加起来计算出前几次之和
                        .reduce(BigDecimal.ZERO,BigDecimal::add);
                BigDecimal lastInterest = lendItem.getExpectAmount().subtract(sumInterest);
                lendItemReturn.setInterest(lastInterest);
            }else {
                //非最后一期
                lendItemReturn.setPrincipal(mapPrincipal.get(currentPeriod));
                lendItemReturn.setInterest(mapInterest.get(currentPeriod));
            }
            lendItemReturn.setTotal(lendItemReturn.getPrincipal().add(lendItemReturn.getInterest()));
            //设置回款状态是否逾期
            lendItemReturn.setOverdue(false);
            lendItemReturn.setFee(new BigDecimal(0));
            lendItemReturn.setReturnDate(lend.getLendStartDate().plusMonths(currentPeriod));
            lendItemReturn.setStatus(0);
            //将回款记录放入回款计划列表
            lendItemReturnList.add(lendItemReturn);
        }
        //批量保存
        lendItemReturnService.saveBatch(lendItemReturnList);
        return lendItemReturnList;
    }
}
